package ro.ciacob.utils {
	import flashx.textLayout.conversion.ConversionType;
	import flashx.textLayout.conversion.TextConverter;
	import flashx.textLayout.edit.EditManager;
	import flashx.textLayout.elements.TextFlow;

	/**
	 *  Collection of string utilities carried on by Grant Skinner.
	 *  Added here my stuff too.
	 *	Claudius Tiberiu Iacob <claudius.iacob@gmail.com>
	 *
	 * 	String Utilities class by Ryan Matsikas, Feb 10 2006
	 *	Visit www.gskinner.com for documentation, updates and more free code.
	 * 	You may distribute this code freely, as long as this comment block remains intact.
	 */
	/**
	 *
	 * @author Claudius
	 */
	public class Strings {
		
		public static const DEFAULT_UIDS_POOL : Object = {};
		
		/**
		 *
		 * @default
		 */
		public static const DEQUOTE_BACKTICK:int = 4;

		/**
		 *
		 * @default
		 */
		public static const DEQUOTE_DOUBLE:int = 1;
		/**
		 *
		 * @default
		 */
		public static const DEQUOTE_SINGLE:int = 2;
		
		/**
		 * Changes "myString", or "my string" into "MY_STRING".
		 * 
		 * Note that you should take all precautions for this functions NOT to 
		 * generate dupplicates (it doesn't do it for you).
		 */
		public static function toAS3ConstantCase(str : String) : String {
			str = deCamelize(str);
			str = str.replace(/\W/g, '_').replace (/_{2,}/g, '_'). toUpperCase();
			return str;
		}
		
		/**
		 * Convenience method to check whether some given string "is" or "equals" any of 
		 * a given set of alternatives.
		 * 
		 * @param	p_string
		 * 			The string to check.
		 * 
		 * @param	p_alternatives
		 * 			The alternatives to check against.
		 * 
		 * @return	True if there is at least one match, false otherwise.
		 */
		public static function isAny (p_string : String, ...p_alternatives) : Boolean {
			for (var i:int = 0; i < p_alternatives.length; i++) {
				var alternative : String = (p_alternatives[i] as String);
				if (p_string == alternative) {
					return true;
				}
			}
			return false;
		}
		
		/**
		 * Changes "myString" into "my string".
		 * 
		 * Note that this doesn't work well with strings containing non 
		 * ASCII chars.
		 */
		public static function deCamelize (str : String) : String {
			return str.replace (/((?<=[a-z0-9])[A-Z])/g, ' $1');
		}
		
		/**
		 * Changes "MY_STRING" into "my string". Note that you should take all
		 * precautions for this functions NOT to generate dupplicates (it doesn't
		 * do it for you).
		 */
		public static function fromAS3ConstantCase(str : String) : String {
			return str.replace(/_+/g, ' ').toLowerCase();
		}

		/**
		 *	Returns everything after the first occurrence of the provided (sub-)string in the string.
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_begin The character or sub-string.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function afterFirst(p_string:String, p_char:String):String {
			if (p_string == null) {
				return '';
			}
			var idx:int = p_string.indexOf(p_char);
			if (idx == -1) {
				return '';
			}
			idx += p_char.length;
			return p_string.substr(idx);
		}

		/**
		 * Represents a given string as a succession of hexadecimal
		 * digits, notated as /(0x)?[0-9a-f]+/ . This is useful for some command line
		 * tools, which require input as hexadecimal numbers.
		 * 
		 * @param	p_string
		 * 			The string to convert
		 * 
		 * @param	prepend0X
		 * 			Whether to add a leading '0x' prefix to each hexadecimal digit
		 * 			(i.e., "0xff" instead of just "ff"); Defaults to false. 
		 * 
  		 * @param	addSpaceBetween
		 * 			Whether to add a whitespace between the resulting
		 * 			hexadecimal numbers, or stick them together. Defaults to false.
		 * 
		 * @return	A string containing hexadecimal numbers. Sample output: "86BD69AB", or "86 BD 69 AB",
		 * 			or "0x860xBD0x690xAB", or "0x86 0xBD 0x69 0xAB".
		 */
		public static function toHexadecimalNotation (
			p_string : String, 
			prepend0X : Boolean = false, 
			addSpaceBetween : Boolean = false
		):String {
			var ret : Array = [];
			if (p_string != null) {
				for (var i:int = 0; i < p_string.length; i++) {
					var char : Number = p_string.charCodeAt(i);
					ret.push (String(prepend0X? '0x' : '').concat(char.toString(16)));
				}
			}
			return ret.join (addSpaceBetween? ' ' : '');
		}
		
		/**
		 *	Returns everything after the last occurence of the provided character in p_string.
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_char The character or sub-string.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function afterLast(p_string:String, p_char:String):String {
			if (p_string == null) {
				return '';
			}
			var idx:int = p_string.lastIndexOf(p_char);
			if (idx == -1) {
				return '';
			}
			idx += p_char.length;
			return p_string.substr(idx);
		}

		/**
		 */
		public static function applyXMLFormatting(obj:*):String {
			var str:String = ('' + obj);
			var xml:XML;
			try {
				xml = XML(str);
				str = xml.toXMLString();
			} catch (e:Error) {
				// Just ignore everything that can't be parsed as XML.
			}
			return str;
		}

		/**
		 *	Returns everything before the first occurrence of the provided character in the string.
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_begin The character or sub-string.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function beforeFirst(p_string:String, p_char:String):String {
			if (p_string == null) {
				return '';
			}
			var idx:int = p_string.indexOf(p_char);
			if (idx == -1) {
				return '';
			}
			return p_string.substr(0, idx);
		}

		/**
		 *	Returns everything before the last occurrence of the provided character in the string.
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_begin The character or sub-string.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function beforeLast(p_string:String, p_char:String):String {
			if (p_string == null) {
				return '';
			}
			var idx:int = p_string.lastIndexOf(p_char);
			if (idx == -1) {
				return '';
			}
			return p_string.substr(0, idx);
		}

		/**
		 *	Determines whether the specified string begins with the specified prefix.
		 *
		 *	@param p_string The string that the prefix will be checked against.
		 *
		 *	@param p_begin The prefix that will be tested against the string.
		 *
		 *	@returns Boolean
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function beginsWith(p_string:String, p_begin:String):Boolean {
			if (p_string == null) {
				return false;
			}
			return p_string.indexOf(p_begin) == 0;
		}

		/**
		 *	Returns everything after the first occurance of p_start and before
		 *	the first occurrence of p_end in p_string.
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_start The character or sub-string to use as the start index.
		 *
		 *	@param p_end The character or sub-string to use as the end index.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function between(p_string:String, p_start:String, p_end:String):String {
			var str:String = '';
			if (p_string == null) {
				return str;
			}
			var startIdx:int = p_string.indexOf(p_start);
			if (startIdx != -1) {
				startIdx += p_start.length; // RM: should we support multiple chars? (or ++startIdx);
				var endIdx:int = p_string.indexOf(p_end, startIdx);
				if (endIdx != -1) {
					str = p_string.substr(startIdx, endIdx - startIdx);
				}
			}
			return str;
		}

		/**
		 *	Description, Utility method that intelligently breaks up your string,
		 *	allowing you to create blocks of readable text.
		 *	This method returns you the closest possible match to the p_delim paramater,
		 *	while keeping the text length within the p_len paramter.
		 *	If a match can't be found in your specified length an  '...' is added to that block,
		 *	and the blocking continues untill all the text is broken apart.
		 *
		 *	@param p_string The string to break up.
		 *
		 *	@param p_len Maximum length of each block of text.
		 *
		 *	@param p_delim delimter to end text blocks on, default = '.'
		 *
		 *	@returns Array
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function block(p_string:String, p_len:uint, p_delim:String = "."):Array {
			var arr:Array = new Array();
			if (p_string == null || !contains(p_string, p_delim)) {
				return arr;
			}
			var chrIndex:uint = 0;
			var strLen:uint = p_string.length;
			var replPatt:RegExp = new RegExp("[^" + escapePattern(p_delim) + "]+$");
			while (chrIndex < strLen) {
				var subString:String = p_string.substr(chrIndex, p_len);
				if (!contains(subString, p_delim)) {
					arr.push(truncate(subString, subString.length));
					chrIndex += subString.length;
				}
				subString = subString.replace(replPatt, '');
				arr.push(subString);
				chrIndex += subString.length;
			}
			return arr;
		}

		/**
		 * Determines whether given string possibly contains meaningful information in it, that is,
		 * letters and/or numbers.
		 *
		 * @param p_string	The string to check
		 * @return	True: at least one alpha numeric char was found.
		 */
		public static function canHaveInfo(p_string:String):Boolean {
			return p_string != null && (((/\w/) as Object).test(p_string));
		}

		/**
		 *	Capitallizes the first word in a string or all words..
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_all (optional) Boolean value indicating if we should
		 *	capitalize all words or only the first.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function capitalize(p_string:String, ... args):String {
			var str:String = trimLeft(p_string);
			// trace('capl', args[0])
			if (args[0] === true) {
				return str.replace(/^.|\b./g, _upperCase);
			} else {
				return str.replace(/(^\w)/, _upperCase);
			}
		}

		/**
		 *	Determines whether the specified string contains any instances of p_char.
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_char The character or sub-string we are looking for.
		 *
		 *	@returns Boolean
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function contains(p_string:String, p_char:String):Boolean {
			if (p_string == null) {
				return false;
			}
			return p_string.indexOf(p_char) != -1;
		}

		/**
		 *	Determines the number of times a charactor or sub-string appears within the string.
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_char The character or sub-string to count.
		 *
		 *	@param p_caseSensitive (optional, default is true) A boolean flag to indicate if the
		 *	search is case sensitive.
		 *
		 *	@returns uint
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function countOf(p_string:String, p_char:String, p_caseSensitive:Boolean = true):uint {
			if (p_string == null) {
				return 0;
			}
			var char:String = escapePattern(p_char);
			var flags:String = (!p_caseSensitive) ? 'ig' : 'g';
			return p_string.match(new RegExp(char, flags)).length;
		}

		/**
		 *
		 * @param p_string
		 * @param mode
		 * @return
		 */
		public static function deQuote(p_string:String, mode:int = -1):String {
			if (mode == -1) {
				mode = (DEQUOTE_DOUBLE | DEQUOTE_SINGLE | DEQUOTE_BACKTICK);
			}
			var remDouble:Boolean = ((mode & DEQUOTE_DOUBLE) == DEQUOTE_DOUBLE);
			var remSingle:Boolean = ((mode & DEQUOTE_SINGLE) == DEQUOTE_SINGLE);
			var remBacktick:Boolean = ((mode & DEQUOTE_BACKTICK) == DEQUOTE_BACKTICK);
			if (remDouble) {
				p_string = p_string.replace(Patterns.DOUBLE_QUOTE_GLOBAL, '');
			}
			if (remSingle) {
				p_string = p_string.replace(Patterns.SINGLE_QUOTE_GLOBAL, '');
			}
			if (remBacktick) {
				p_string = p_string.replace(Patterns.BACKTICK_GLOBAL, '');
			}
			return p_string;
		}

		/**
		 *	Levenshtein distance (editDistance) is a measure of the similarity between two strings,
		 *	The distance is the number of deletions, insertions, or substitutions required to
		 *	transform p_source into p_target.
		 *
		 *	@param p_source The source string.
		 *
		 *	@param p_target The target string.
		 *
		 *	@returns uint
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function editDistance(p_source:String, p_target:String):uint {
			var i:uint;

			if (p_source == null) {
				p_source = '';
			}
			if (p_target == null) {
				p_target = '';
			}

			if (p_source == p_target) {
				return 0;
			}

			var d:Array = new Array();
			var cost:uint;
			var n:uint = p_source.length;
			var m:uint = p_target.length;
			var j:uint;

			if (n == 0) {
				return m;
			}
			if (m == 0) {
				return n;
			}

			for (i = 0; i <= n; i++) {
				d[i] = new Array();
			}
			for (i = 0; i <= n; i++) {
				d[i][0] = i;
			}
			for (j = 0; j <= m; j++) {
				d[0][j] = j;
			}

			for (i = 1; i <= n; i++) {

				var s_i:String = p_source.charAt(i - 1);
				for (j = 1; j <= m; j++) {

					var t_j:String = p_target.charAt(j - 1);

					if (s_i == t_j) {
						cost = 0;
					} else {
						cost = 1;
					}

					d[i][j] = _minimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
				}
			}
			return d[n][m];
		}

		/**
		 *	Determines whether the specified string ends with the specified suffix.
		 *
		 *	@param p_string The string that the suffic will be checked against.
		 *
		 *	@param p_end The suffix that will be tested against the string.
		 *
		 *	@returns Boolean
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function endsWith(p_string:String, p_end:String):Boolean {
			return p_string.lastIndexOf(p_end) == p_string.length - p_end.length;
		}

		/**
		 *
		 * @param p_pattern
		 * @return
		 */
		public static function escapePattern(p_pattern:String):String {
			return p_pattern.replace(/(\$|\]|\[|\{|\}|\(|\)|\*|\+|\?|\.|\\)/g, '\\$1');
		}

		/**
		 * Generates a random id, which is unique in a given context.
		 * @param	uniqueIdsPool
		 * @param	idLength
		 * @return
		 */
		public static function generateUniqueId(uniqueIdsPool:Object, idLength:int = 2):String {
			if (uniqueIdsPool == null) {
				uniqueIdsPool = DEFAULT_UIDS_POOL;
			}
			var ret:String = "";
			var chars:String = "abcdefghijklmnopqrstuvxwzABCDEFGHIJKLMNOPQRSTUVXWZ0123456789";
			var counter:int = 0;
			do {
				do {
					var char:String = chars.charAt(Math.round(Math.random() * (chars.length - 1)));
					ret += char;
					counter++;
				} while (counter < idLength);
				if (uniqueIdsPool[ret] === undefined) {
					uniqueIdsPool[ret] = null;
					break;
				} else {
					var poolSize:Number = 0;
					for (var tmp:String in uniqueIdsPool) {
						poolSize++;
					}
					var arePermutationsExhausted:Boolean = (poolSize == NumberUtil.computePermutationsNumber(chars.
						length, idLength));
					if (arePermutationsExhausted) {
						throw(new Error('UID Generator: cannot generate an unique id; All combinations exhausted.'));
					}
				}
			} while (true);
			return ret;
		}

		/**
		 *	Determines whether the specified string contains text.
		 *
		 *	@param p_string The string to check.
		 *
		 *	@returns Boolean
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function hasText(p_string:String):Boolean {
			var str:String = removeExtraWhitespace(p_string);
			return !!str.length;
		}

		/**
		 *	Determines whether the specified string contains any characters.
		 *
		 *	@param p_string The string to check
		 *
		 *	@returns Boolean
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function isEmpty(p_string:String):Boolean {
			if (p_string == null) {
				return true;
			}
			return !p_string.length;
		}

		/**
		 *	Determines whether the specified string is numeric.
		 *
		 *	@param p_string The string.
		 *
		 *	@returns Boolean
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function isNumeric(p_string:String):Boolean {
			if (p_string == null) {
				return false;
			}
			var regx:RegExp = /^[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$/;
			return regx.test(p_string);
		}

		/**
		 * Pads p_string with specified character to a specified length from the left.
		 *
		 *	@param p_string String to pad
		 *
		 *	@param p_padChar Character for pad.
		 *
		 *	@param p_length Length to pad to.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function padLeft(p_string:String, p_padChar:String, p_length:uint):String {
			var s:String = p_string;
			while (s.length < p_length) {
				s = p_padChar + s;
			}
			return s;
		}

		/**
		 * Pads p_string with specified character to a specified length from the right.
		 *
		 *	@param p_string String to pad
		 *
		 *	@param p_padChar Character for pad.
		 *
		 *	@param p_length Length to pad to.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function padRight(p_string:String, p_padChar:String, p_length:uint):String {
			var s:String = p_string;
			while (s.length < p_length) {
				s += p_padChar;
			}
			return s;
		}

		/**
		 *	Properly cases' the string in "sentence format".
		 *
		 *	@param p_string The string to check
		 *
		 *	@returns String.
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function properCase(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			var str:String = p_string.toLowerCase().replace(/\b([^.?;!]+)/, capitalize);
			return str.replace(/\b[i]\b/, "I");
		}

		/**
		 *	Escapes all of the characters in a string to create a friendly "quotable" sting
		 *
		 *	@param p_string The string that will be checked for instances of remove
		 *	string
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function quote(p_string:String):String {
			var regx:RegExp = /[\\"\r\n]/g;
			return '"' + p_string.replace(regx, _quote) + '"'; //"
		}

		/**
		 *	Removes instances of the remove string in the input string.
		 *
		 *	@param p_string The string that will be checked for instances of remove
		 *	string
		 *
		 *	@param p_remove The string that will be removed from the input string.
		 *
		 *	@param p_caseSensitive An optional boolean indicating if the replace is case sensitive. Default is true.
		 *
		 *  @param p_firstOnly An optional boolean indicating whether to remove the first occurence only, or all of them.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function remove(p_string:String, p_remove:String, p_caseSensitive:Boolean = true, p_firstOnly:Boolean =
			false):String {
			if (p_string == null) {
				return '';
			}
			var rem:String = escapePattern(p_remove);
			var gFlag:String = (!p_firstOnly) ? 'g' : '';
			var flags:String = (!p_caseSensitive) ? 'i' : '';
			flags += gFlag;
			return p_string.replace(new RegExp(rem, flags), '');
		}

		/**
		 *	Removes extraneous whitespace (extra spaces, tabs, line breaks, etc) from the
		 *	specified string.
		 *
		 *	@param p_string The String whose extraneous whitespace will be removed.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function removeExtraWhitespace(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			var str:String = trim(p_string);
			return str.replace(/\s+/g, ' ');
		}

		/**
		 */
		public static function removeNewLines(obj:*):String {
			var str:String = ('' + obj);
			str = Strings.trim(str);
			str = str.replace(/[\n\r]/g, ' ');
			str = str.replace(/\s{2,}/g, ' ');
			return str;
		}

		/**
		 * Repeats a given string a number of times and returns the result.
		 *
		 * @param	string
		 * 			String to repeat.
		 * @param	times
		 * 			The number of times to repeat.
		 * @return	The given string, concatenated with itself the given number of times.
		 */
		public static function repeatString(string:String, times:int):String {
			var ret:Array = [];
			for (var i:int = 0; i < times; i++) {
				ret.push(string);
			}
			return ret.join('');
		}

		/**
		 *	Returns the specified string in reverse character order.
		 *
		 *	@param p_string The String that will be reversed.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function reverse(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			return p_string.split('').reverse().join('');
		}

		/**
		 *	Returns the specified string in reverse word order.
		 *
		 *	@param p_string The String that will be reversed.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function reverseWords(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			return p_string.split(/\s+/).reverse().join('');
		}

		/**
		 *	Determines the percentage of similiarity, based on editDistance
		 *
		 *	@param p_source The source string.
		 *
		 *	@param p_target The target string.
		 *
		 *	@returns Number
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function similarity(p_source:String, p_target:String):Number {
			var ed:uint = editDistance(p_source, p_target);
			var maxLen:uint = Math.max(p_source.length, p_target.length);
			if (maxLen == 0) {
				return 100;
			} else {
				return (1 - ed / maxLen) * 100;
			}
		}

		/**
		 *	Remove's all < and > based tags from a string
		 *
		 *	@param p_string The source string.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function stripTags(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			return p_string.replace(/<\/?[^>]+>/igm, '');
		}

		/**
		 *	Swaps the casing of a string.
		 *
		 *	@param p_string The source string.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function swapCase(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			return p_string.replace(/(\w)/, _swapCase);
		}

		/**
		 * Similar to `tlfMarkupToText`, but yelds the TextField's proprietary HTML format instead.
		 * @see tlfMarkupToText()
		 */
		public static function tlfMarkupToHTML(markup:String, numPeelLevels:int = 0):String {
			var flow:TextFlow = TextConverter.importToFlow(markup, TextConverter.TEXT_LAYOUT_FORMAT);
			if (flow != null) {
				var text:String = (TextConverter.export(flow, TextConverter.TEXT_FIELD_HTML_FORMAT, ConversionType.STRING_TYPE) as
					String);
				if (numPeelLevels > 0) {
					var xml:XML;
					try {
						XML.ignoreWhitespace = true;
						XML.ignoreComments = true;
						XML.ignoreProcessingInstructions = true;
						xml = XML(text);
						while (numPeelLevels > 0) {
							if (xml.children().length() > 1) {
								break;
							}
							xml = xml.children()[0];
							numPeelLevels--;
						}
						text = xml.toXMLString();
						text = Strings.removeNewLines(text);
					} catch (e:Error) {
						//trace('Strings::tlfMarkupToHTML() - cannot create XML object from `', text, '`.');
					}
				}
				return trim(text);
			}
			return markup;
		}

		/**
		 * Strips formatting information from a give TLF markup, yelding unformatted text.
		 * Returns an empty string if conversion fails.
		 *
		 * @param	markup
		 * 			The markup to be converted. Conversion is done via the TextConverter
		 * 			class.
		 *
		 * @return	The plain text in the given markup, if the conversion succeeds;
		 * 			or, the original text, if the given markup IS NOT a valid TLF markup;
		 * 			or, an empty string, if the given markup IS a valid TLF markup, but
		 * 			conversion fails.
		 * @see flashx.textLayout.conversion.TextConverter
		 */
		public static function tlfMarkupToText(markup:String):String {
			var flow:TextFlow = TextConverter.importToFlow(markup, TextConverter.TEXT_LAYOUT_FORMAT);
			if (flow != null) {
				var text:String = (TextConverter.export(flow, TextConverter.PLAIN_TEXT_FORMAT, ConversionType.STRING_TYPE) as
					String);
				return trim(text);
			}
			return markup;
		}


		/**
		 *	Removes whitespace from the front and the end of the specified
		 *	string.
		 *
		 *	@param p_string The String whose beginning and ending whitespace will
		 *	will be removed.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function trim(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			return p_string.replace(/^\s+|\s+$/g, '');
		}

		/**
		 *	Removes whitespace from the front (left-side) of the specified string.
		 *
		 *	@param p_string The String whose beginning whitespace will be removed.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function trimLeft(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			return p_string.replace(/^\s+/, '');
		}

		/**
		 *	Removes whitespace from the end (right-side) of the specified string.
		 *
		 *	@param p_string The String whose ending whitespace will be removed.
		 *
		 *	@returns String	.
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function trimRight(p_string:String):String {
			if (p_string == null) {
				return '';
			}
			return p_string.replace(/\s+$/, '');
		}

		/**
		 *	Returns a string truncated to a specified length with optional suffix
		 *
		 *	@param p_string The string.
		 *
		 *	@param p_len The length the string should be shortend to
		 *
		 *	@param p_suffix (optional, default=...) The string to append to the end of the truncated string.
		 *
		 *	@returns String
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function truncate(p_string:String, p_len:uint, p_suffix:String = "..."):String {
			if (p_string == null) {
				return '';
			}
			p_len -= p_suffix.length;
			var trunc:String = p_string;
			if (trunc.length > p_len) {
				trunc = trunc.substr(0, p_len);
				if ((/[^\s]/ as Object).test(p_string.charAt(p_len))) {
					trunc = trimRight(trunc.replace(/\w+$|\s+$/, ''));
				}
				trunc += p_suffix;
			}

			return trunc;
		}

		/**
		 *	Determins the number of words in a string.
		 *
		 *	@param p_string The string.
		 *
		 *	@returns uint
		 *
		 * 	@langversion ActionScript 3.0
		 *	@playerversion Flash 9.0
		 *	@tiptext
		 */
		public static function wordCount(p_string:String):uint {
			if (p_string == null) {
				return 0;
			}
			return p_string.match(/\b\w+\b/g).length;
		}

		/**
		 * Similar to the "wrap()" function in PHP. Wraps the given string into lines (most usefull for logs
		 * or consoles). Note that there is no "breakWord" argument. Words longer than maximum line length 
		 * will always be broken.
		 *
		 * @param p_string		The strin gto wrap.
		 *
		 * @param p_maxLen		The maximum length of a line, in chars. Optional, defaults to 70.
		 *
		 * @param p_delimiters	An array with chars (strings of length "one") that can be used as delimiters
		 * 						for breaking the string. Optional, defaults to one whitespace.
		 *
		 * @return				An array with lines of text. Returns an empty array for null or empty strings
		 * 						(white space don't count).
		 */
		public static function wrap(p_string:String, p_maxLen:int = 70, p_delimiters:Array = null):Array {
			var lines:Array = [];
			// Default delimiter is a space
			if (p_delimiters == null) {
				p_delimiters = [' '];
			}
			p_string = trim(p_string);
			if (!isEmpty(p_string)) {
				// Tokenize the string
				var tokens : Array = [];
				var currToken : Array = [];
				for (var i:int = 0; i < p_string.length; i++) {
					var currChar : String = p_string.charAt(i);
					currToken.push(currChar);
					var isDelimiter : Boolean;
					for (var j:int = 0; j < p_delimiters.length; j++) {
						if (currChar == p_delimiters[j]) {
							isDelimiter = true;
							break;
						}
					}
					if (isDelimiter || (i == p_string.length - 1)) {
						isDelimiter = false;
						while (currToken.length > p_maxLen) {
							var slice : Array = currToken.slice(0, p_maxLen);
							tokens.push(slice);
							currToken.splice (0, p_maxLen);
						}
						if (currToken.length > 0) {
							tokens.push(currToken);
						}
						currToken = [];
					}
				}
				// Attempt to place tokens in lines
				var line : String = '';
				while (tokens.length > 0) {
					var token : Array = tokens[0];
					if (line.length + token.length <= p_maxLen) {
						line += token.join ('');
						tokens.splice (0, 1);
					} else {
						if (line.length > 0) {
							lines.push(line);
							line = '';
						}
					}
				}
				if (line.length > 0) {
					lines.push(line);
				}
			}
			return lines;
		}

		private static function _minimum(a:uint, b:uint, c:uint):uint {
			return Math.min(a, Math.min(b, Math.min(c, a)));
		}

		private static function _quote(p_string:String, ... args):String {
			switch (p_string) {
				case "\\":
					return "\\\\";
				case "\r":
					return "\\r";
				case "\n":
					return "\\n";
				case '"':
					return '\\"';
				default:
					return '';
			}
		}

		private static function _swapCase(p_char:String, ... args):String {
			var lowChar:String = p_char.toLowerCase();
			var upChar:String = p_char.toUpperCase();
			switch (p_char) {
				case lowChar:
					return upChar;
				case upChar:
					return lowChar;
				default:
					return p_char;
			}
		}

		private static function _upperCase(p_char:String, ... args):String {
			// trace('cap latter ', p_char)
			return p_char.toUpperCase();
		}
	}
}
